package com.innovation.ic.cc.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.Claim;
import com.google.common.base.Strings;
import com.innovation.ic.cc.base.model.cc.Client;
import com.innovation.ic.cc.base.pojo.constant.Constants;
import com.innovation.ic.cc.base.pojo.constant.HttpHeader;
import com.innovation.ic.cc.base.pojo.constant.handler.RedisStorage;
import com.innovation.ic.cc.base.pojo.global.Context;
import com.innovation.ic.cc.base.value.FilterParamConfig;
import com.innovation.ic.cc.base.value.RunEnvConfig;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 验证header中是否有Authorization和Token属性，登录相关的接口不校验token信息
 */
@Component
public class AccessFilter implements GlobalFilter, Ordered {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RunEnvConfig runEnvConfig;

    @Resource
    private FilterParamConfig config;

    private static FilterParamConfig filterParamConfig;

    @PostConstruct
    public void init() {
        filterParamConfig = config;
    }

    /**
    /**
     * 过滤规则：
     * 1.是否是允许通过的路径。
     * 2.header中是否有clientId，是否合法。
     * 3.header中是否有token，token格式为"Bearer "。如果有，则调用erp的接口判断是否有效。
     * @param exchange exchange
     * @param chain chain
     * @return 返回结果
     */
    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        // 如果是允许直接通过了路径，则放行
        String path = request.getPath().toString();
        List<String> allowPathList = filterParamConfig.getAllowPathList();
        for (int i = 0; i < allowPathList.size(); i++) {
            if (path.contains(allowPathList.get(i))) {
                logger.info("请求【" + path + "】中包含路径【" + allowPathList.get(i) + "】，直接放行");
                return chain.filter(exchange);
            }
        }

        // 判断是否是登录接口，登录接口直接放行
        List<String> loginPathList = filterParamConfig.getLoginPathList();
        for (int i = 0; i < loginPathList.size(); i++) {
            if (path.contains(loginPathList.get(i))) {
                logger.info("请求【" + path + "】中包含路径【" + loginPathList.get(i) + "】，登录相关接口调用直接放行");
                return chain.filter(exchange);
            }
        }

        // 判断是否是验证码接口，验证码接口直接放行
        List<String> verificationCodePathList = filterParamConfig.getVerificationCodePathList();
        for (int i = 0; i < verificationCodePathList.size(); i++) {
            if (path.contains(verificationCodePathList.get(i))) {
                logger.info("请求【" + path + "】中包含路径【" + verificationCodePathList.get(i) + "】，验证码接口调用直接放行");
                return chain.filter(exchange);
            }
        }

        HttpHeaders httpHeaders = request.getHeaders();
        Set<String> httpHeaderKeySet = httpHeaders.keySet();
        if (null != httpHeaders && !httpHeaders.isEmpty()){
            // 验证header中的clientId，key为authorization
            Set<String> clientSet = (Set<String>) Context.get(RedisStorage.CLIENT);
            if (!httpHeaderKeySet.contains(filterParamConfig.getAuthorization())){
                logger.warn("请求的header中没有{}", filterParamConfig.getAuthorization());
                return null;
            } else {
                String clientId = httpHeaders.get(filterParamConfig.getAuthorization()).get(0);
                Iterator clientIterator = clientSet.iterator();
                Client client;
                boolean clientIdExist = false;
                while (clientIterator.hasNext()){
                    client = JSON.parseObject((String) clientIterator.next(), Client.class);
                    if (clientId.equals(client.getId())){
                        clientIdExist = true;
                        break;
                    }
                }
                if (!clientIdExist){
                    logger.warn("header中的clientId参数错误");
                    return null;
                } else {
                    logger.info("header中的clientId参数为{}", clientId);
                }
            }

            // 验证header中的token，key是token
            if(!httpHeaderKeySet.contains(filterParamConfig.getToken())){
                logger.warn("请求的header中没有{}", filterParamConfig.getToken());
                return null;
            } else {
                String tokenString = httpHeaders.get(filterParamConfig.getToken()).get(0);
                String token = tokenString.split(HttpHeader.TOKEN_SPLIT)[1];

                // 校验token是否有效
                Boolean result = judgeTokenIfEffective(token);
                if(!result){
                    logger.warn("header中的token参数不合法");
                    return null;
                }else{
                    logger.info("header中的token校验通过");
                }
            }
        } else {
            logger.warn("请求的header是空");
            return null;
        }

        return chain.filter(exchange);
    }

    /**
     * 校验token是否有效
     * @param token token信息
     * @return 返回校验结果
     */
    private Boolean judgeTokenIfEffective(String token) {
        Boolean result = Boolean.FALSE;
        if(!Strings.isNullOrEmpty(token)){
            try {
                // 解密token
                Map<String, Claim> claims = JWT.decode(token).getClaims();
                if(!claims.isEmpty()){
                    logger.info("当前请求token有效,校验成功");
                    result = Boolean.TRUE;
                }else{
                    logger.info("token解密失败,校验token失败");
                }
            }catch (Exception e){
                logger.error("token校验失败,原因:", e);
            }
        }else{
            logger.info("当前请求token为空");
        }

        if(!result && !Constants.PROD_RUN_ENVIRONMENT.equals(runEnvConfig.getEnv())){
            logger.info("当前环境参数为:[{}],token无效时不拦截请求", runEnvConfig.getEnv());
            result = true;
        }

        return result;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}